<!DOCTYPE html>
<html lang="zh-CN">

<head>
	<meta charset="UTF-8" />
	<title>HeadFirstGo</title>
	<link rel="stylesheet" href="../style/css/article.css">
	<!-- <script src="js/a.js"></script> -->
</head>

<body>
<a id="top" href="#"></a>
<div id="particles-js"></div>
</div>
<div class="text">
<h1>Text</h1>
<div class="h2i1">
<h2>前言 如何使用本书</h2>
<ul>
<li>你想要记住一些东西, 首先要让你的大脑觉得它很重要</li>
<li>Head First准则</li>
<ul>
<li>可视化知识, 图像优于文字</li>
<li>使用对话和个性化的风格</li>
<li>让学习者更深入的思考, 充分调动其情绪</li>
</ul>
<li>元认知, 我们中的大多数人在成长过程中没有学过元认知或学习理论的课程. 我们想去学习, 但很少有人教我们如何学习</li>
</ul>
</div><div class="h2i2">
<h2>1 让我们开始吧 语法基础</h2>
<ul>
<li>快速编译, 简洁, GC, 并发, 多核</li>
<li>命令行工具, Web服务器</li>
<li>package main, 想要直接运行, 请叫这个包名</li>
<li>import, 导的越多, 最后的程序越大, 所以只导入必要的包</li>
<li>go fmt 工具</li>
<li>字符串字面量由双引号(")包围, 但rune字面量由单引号(')包围, 这是规范么?</li>
<li>Go程序几乎可以使用地球上任何语言的任何字符, 因为Go使用Unicode标准来存储rune. rune被保存为数字代码, 而不是字符本身, 如果你把rune传递给fmt.Println, 你会在输出中看到数字代码, 而不是原始字符</li>
<li>Go是静态类型的. 如果你在错误的位置使用了错误的值类型, Go会告诉你</li>
<li>你可以通过将任何值传递给reflect包的TypeOf函数, 来查看它们的类型</li>
</ul>
</div><div class="h2i3">
<h2>2 接下来运行哪些代码 条件和循环</h2>
<ul>
<li>区分函数与方法, 为特定类型绑定的是方法</li>
<li>time包, time.Now(), 返回一个time.Time类型的变量</li>
<li>now := time.Now(); fmt.Println(now.Year())</li>
<li>strings包有一个Replacer类型, 可以在字符串中搜索子字符串, 并且在每次该子字符串出现的地方用另一个字符串替换它</li>
<ul>
<li>这不就是我之前找的中文标点转英文的功能嘛</li>
<li>replacer := strings.NewReplacer("#", "o")</li>
<li>newStr := replacer.Replace(oldStr)</li>
</ul>
</ul>
<hr/>
<ul>
<li>reader := bufio.NewReader(os.Stdin)</li>
<li>input, err := reader.ReadString('\n')</li>
<li>log.Fatal = fmt.Print + os.Exit()</li>
<li>fileInfo, err := os.Stat("a.txt"), 返回一个os.FileInfo</li>
<li>fileInfo.Size()查看文件大小</li>
</ul>
<hr/>
<ul>
<li>go为了简洁, 关键字很少, 比如int都不是关键字, 你可以自定义一个变量叫做int</li>
<li>这个变量会覆盖掉外层int的所有功能, 就近嘛, 你懂的</li>
<li>比如error, append, fmt, 都可以作为变量名, 但我们是不会用的, 是吧</li>
</ul>
<hr/>
<ul>
<li>strings.TrimSpace(), 删除str开头和结尾的所有空白(space\n\t)</li>
<li>strconv.ParseFloat(str, 64), string -> float64, 尽量不使用float32</li>
<li>seconds := time.Now().Unix(), 返回的是秒</li>
<li>rand.Seed(seconds)</li>
<li>math/rand包, rand.Intn(100), 返回[0,100)的整数</li>
<li>区分导入路径和包名, 导入路径是import时如何找到这个文件夹, 调用函数时使用的包名是源文件package后面写的东西, 一遍我们习惯和文件夹同名, 也就是导入路径的最后一部分</li>
</ul>
</div><div class="h2i4">
<h2>3 调用 函数</h2>
<ul>
<li>Printf和Sprintf</li>
<ul>
<li>%s(string), %t(bool), %v(任何值)</li>
<li>%#v(任何值, 源代码中原样输出)</li>
<li>%T(type), %%想打印百分号</li>
<li>%f默认6位, so, %.2f</li>
<li>%5.3f, 5是整个数的最小宽度, .3是小数点后的宽度</li>
</ul>
<li>例如 str := "\t", %v就是个tab, %#v就原样输出"\t", %s也是个tab</li>
</ul>
<hr/>
<ul>
<li>错误处理, 自定义一个错误</li>
<li>err := errors.New("your message")</li>
<li>fmt.Println(err.Error())</li>
<li>如果要将错误值传递给fmt或log包中的函数, 则可能不需要调用它的Error方法. fmt和log中的函数已经被编写成能够检查是否传递给它们的值有Error方法, 如果有, 则打印Error的返回值</li>
<li>err := fmt.Errorf("error: %d\n", a), 想在error信息里插入动态信息, 可以用这个方法, 返回一个错误</li>
<li>go是一种值传递语言, 想要改我么, 了解下指针, </li>
<li>与其他语言不同, 在Go中, 一个函数返回一个指向函数局部变量的指针是可以的. 即使该变量不在作用域内, 只要你仍然拥有指针, Go将确保你仍然可以访问该值</li>
<li>C/C++不行, 函数的局部变量在栈上, 函数没了栈回收了, 返回个指针不就成了野指针了么, golang有内存逃逸</li>
</ul>
</div><div class="h2i5">
<h2>4 代码集 包</h2>
<ul>
<li>bin, 保存已编译的二进制可执行程序</li>
<li>pkg, 保存已编译的二进制包文件</li>
<li>希望是个可执行程序, 入口源文件, 就叫main包</li>
<li>希望是个工具包而非一个程序, 就叫其他名字</li>
<li>包命名规范</li>
<ul>
<li>包名应全部为小写</li>
<li>如果含义相当明显, 名称应该缩写(如fmt)</li>
<li>如果可能的话, 应该是一个词. 如果需要两个词, 不应该用下划线分隔, 第二个词也不应该大写. (strconv包就是一个例子. )</li>
<li>导入的包名可能与本地变量名冲突, 所以不要使用包用户可能也想使用的名称. (例如, 如果fmt包被命名为format, 那么导入该包的任何人如果把一个局部变量也命名为format, 则将面临冲突的风险)</li>
</ul>
<li>go install + 目录名, 把程序编译放在bin里, 然后把bin配置到环境变量里, 就可以在哪里都随意使用了, 我的mdToHtml就不用到处复制了</li>
<li>Mac/Linux, export GOPATH="/code", Windows, set GOPATH="D:\code"</li>
<li>包的发布, 各个人之间都有个叫hello的包, 区分很麻烦</li>
<li>go/src/github.com/liws521/包名, 每个人的就能区分了</li>
<li>go get + 包的url路径, 下载到src里</li>
<li>go doc + 包的导入路径, 查看包的文档</li>
<ul>
<li>package之前的写包的功能, 包注释应以"Package"开头, 后跟包名</li>
<li>每个函数上面写函数的描述, 函数注释应以它们描述的函数的名称开头</li>
</ul>
<li>godoc工具, 注意和go doc区分, 生成html文档, godoc -http=:6060</li>
<ul>
<li>然后浏览器, http://localhost:6060/pkg就看到了</li>
</ul>
</ul>
</div><div class="h2i6">
<h2>5 列表 数组</h2>
<ul>
<li>数组字面量, a := [3]int{1, 2, 3}</li>
<li>可以将数组字面量分散到多行上, 但是你必须在代码中的每个换行字符前使用逗号</li>
<ul>
<li>以编译原理的角度考虑问题, 这个逗号告诉编译器别给我加分号了</li>
</ul>
<li>可以把array, slice, map直接传给fmt包, 他知道如果打印</li>
<li>数组按%#v打印就是数组字面量</li>
</ul>
<hr/>
<ul>
<li>file, err := os.Open("a.txt")</li>
<li>scanner := bufio.NewScanner(file)</li>
<li>for scanner.Scan() { fmt.Println(scanner.Text()) }</li>
<ul>
<li>Scan读取一行, Text是一个没有\n的string</li>
</ul>
<li>if scanner.Err() != nil {如果扫描文件时有错误}</li>
<li>err = file.Close(), Close会返回err, 一直不知道</li>
</ul>
</div><div class="h2i7">
<h2>6 追加的问题 切片</h2>
<ul>
<li>切片字面量, a := []int{1, 2, 3}</li>
<li>切片是底层数组的一个视图, 想想聚光灯, 想想PS的图层, 底层数组存在, 通过切片把其中的一段展示在聚光灯下</li>
<li>在其他的语言中, 需要在使用切片变量之前先测试它是否包含切片. 但是在Go中, 函数有意被写成对待nil的切片就像它是一个空切片一样. 例如, 如果输入参数是一个nil切片那么len返回0</li>
<li>其他语言中, 一个引用如果为空, 要特殊处理一下, golang对没有make的切片(nil切片)做了特殊的处理, len为0, append也把nil切片当空切片</li>
</ul>
<hr/>
<ul>
<li>os.Args切片, 0号程序名</li>
<li>可变长参数, func a(b int, c ...int)</li>
<ul>
<li>多余1个的int都放在c里, c是一个切片</li>
<li>向可变长参数可以传入一个切片, 调用时在切片后面加...</li>
</ul>
<li>math.Inf(-1), float64的负无穷</li>
</ul>
</div><div class="h2i8">
<h2>7 标签数据 映射</h2>
<ul>
<li>map字面量, myMap := map[string]int{"a": 1, "b": 2}</li>
<li>通过make的方式, myMap := make(map[string]int)</li>
<li>字面量方式创建空map, myMap := map[string]int{}</li>
<li>添加和修改, myMap["a"] = 1</li>
<li>查询, value, ok = myMap["a"], ok为真, 有这个记录, ok为假, 没这个记录, 返回的value只不过是默认的零值</li>
<li>删除, delete(myMap, "a")</li>
<li>遍历, map的for-range是每次随机顺序的, 想要以某种顺序遍历, 先遍历一遍把所有的key存到一个切片里, 然后用sort以指定规则对这个切片排序, 遍历这个切片, 一个一个向map查其相应的value</li>
</ul>
</div><div class="h2i9">
<h2>8 构建存储 struct</h2>
<ul>
<li>type cat struct{field type}</li>
<li>var cat1 cat, cat是我们自定义的一种类型, cat1是一个变量, 类与对象</li>
<li>struct是一种值类型, 想要修改请传指针, 作为函数参数传递也尽量传指针, 不拷贝, 效率高</li>
<li>(*p).field是对的, *p.field是不对的, 因为点的优先级更高</li>
<li>可以省略写, p.field就行, 编译器会自动补全</li>
<li>无论是类型名还是字段名, 想跨包使用就大写, export</li>
<li>struct字面量, myCat := cat{name: "Tom", age: 20}, 创建实例的时候直接复制</li>
<li>一个struct A里嵌入另一个struct B, 通过a.b.field来访问b的字段</li>
<li>如果嵌入的B是匿名的, 可以通过a.B.field, 也可以直接a.field, 嵌入struct的字段被提升到了外部struct, 你可以像访问外部struct的字段一样访问它们</li>
</ul>
</div><div class="h2i10">
<h2>9 我喜欢的类型 定义类型</h2>
<ul>
<li>Go经常使用struct作为基础类型来定义类型, 但是它们也能基于int, string, bool或者其他任何类型</li>
<li>type myint int64, 使用int64作为基础类型, 定义一个我自己的新类型, 我可以赋予这种类型语义, 可以为它绑定方法</li>
<li>int64和myint是两种不同的类型, 彼此之间不能直接赋值, 要通过类型转换</li>
<li>我定义的新类型与字面值运算是可以的, 比如10, 和另一个类型的值运算是不可以的</li>
<li>func (m *MyType) funcName() {}, 调用时mytype.funcName, m是接收器, 类似于其他语言中的self/this, 一般用类型名的小写首字母</li>
<li>为了在原变量上操作而非在拷贝上操作, 方法这里一般也传指针, 调用时用变量调用就行, 编译器会自动转换</li>
</ul>
</div><div class="h2i11">
<h2>10 保密 封装和嵌入</h2>
<ul>
<li>封装, setter, SetXXX格式, 在赋值前可以进行各种合理性检查, getter, GetXXX格式, 或者直接XXX, 不写Get</li>
<li>A中嵌入了一个匿名B, B中大写的字段方法在A中都能用, 都被提升到了A层, 小写的仍然不能访问, 不提升</li>
<li>unicode/utf8, RuneCountInString函数, 返回String中的Rune数</li>
<li>我想用B的方法, 就嵌入一个匿名的B, 像其他语言的类的继承一样, 随便用了</li>
</ul>
</div><div class="h2i12">
<h2>11 你能做什么 接口</h2>
<ul>
<li>type myinter interface {许多方法}</li>
<li>接口不关心你是什么, 不关心你的数据, 只关心你能做什么, 只关心方法</li>
<li>只要实现了所有方法, 就实现了这个接口</li>
<li>类型断言, 变量, ok := 接口类型变量.(具体类型)</li>
<li>error就是个接口, 只有一个Error方法</li>
<li>为什么error可以小写而不用导入任何包?</li>
<ul>
<li>error类型像int或者string一样是一个"预定义标识符", 它不属于任何包. 它是"全局块"的一部分, 这意味着它在任何地方可用, 不用考虑当前包信息</li>
</ul>
<li>fmt.Stringer接口, 每个类型都搞个String方法, 实现一下这个接口, fmt输出时就知道如何输出了</li>
<li>interface{}, 空接口类型, 什么类型都能装, 不能调用任何方法</li>
</ul>
</div><div class="h2i13">
<h2>12 重新站起来 从失败中恢复</h2>
<ul>
<li>用defer确保文件得以关闭, defer只能加函数/方法</li>
<li>io/ioutil包包含一个ReadDir函数, 它允许我们读取目录内容. 向ReadDir传递一个目录的名称, 它将返回一个值切片, 每个值切片对应目录包含的每个文件或子目录(以及遇到的任何错误), 每个切片的值都满足FileInfo接口</li>
<li>当程序出现panic时, 当前函数停止运行, 程序打印日志消息并崩溃</li>
<li>主动panic, panic("your message")</li>
<li>panic是会打印调用堆栈, 比较直观</li>
<li>panic时, 先处理完defer栈, 才崩溃</li>
<li>Go提供了一个内置的recover函数, 可以阻止程序陷入panic. 我们需要使用它来体面地退出程序</li>
<li>如果在程序处于panic状态时调用recover, 它将停止panic. 但是当你在函数中调用panic时, 该函数将停止执行. 因此, 在panic所在的同一函数中调用recover没有意义, 因为panic无论如何都会继续</li>
<li>把recover写在一个函数里, defer这个函数, 出现panic会执行defer, 就能执行到recover了</li>
<li>调用recover不会导致在出现panic时恢复执行, 至少不会完全恢复. 产生panic的函数将立即返回, 而该函数块中panic之后的任何代码都不会执行. 但是, 在产生panic的函数返回之后, 正常的执行将恢复</li>
<li>在正常程序执行过程中调用recover时, 它只返回nil, 而不执行其他操作</li>
<li>但是当出现panic时, recover返回传递给panic的任何值. 这可以用来收集有关panic的信息, 帮助恢复或向用户报告错误</li>
<li>panic, recover这种少用, 尽量别用, 其他语言的exception思想也不要带到golang</li>
<li>golang里尽量用if err != nil + return</li>
</ul>
</div><div class="h2i14">
<h2>13 goroutine和channel</h2>
<ul>
<li>在Go中, 并发任务称为goroutine. 其他编程语言有一个类似的概念, 叫作线程, 但是goroutine比线程需要更少的计算机内存, 启动和停止的时间更少, 这意味着你可以同时运行更多的goroutine</li>
<li>main执行结束, go程序也结束, 所以让它等一等, time.Sleep(time.Second)</li>
<li>mychan := make(chan int), 通过channel进行通信</li>
<li>无缓冲的channel和有缓冲的channel</li>
</ul>
</div><div class="h2i15">
<h2>14 代码的质量保证 自动化测试</h2>
<ul>
<li>写一个a_test.go文件, 引入testing包, 用go test工具</li>
<li>你不需要将测试代码与正在测试的代码放在同一个包中, 但是如果你想从包中访问未导出的类型或函数, 则需要这样做</li>
<li>测试函数叫TestXXX, 参数为*testing.T</li>
<li>测试驱动开发, 编写测试, 确保通过, 重构代码</li>
<li>go test -run 部分名称, 指定测试部分函数</li>
</ul>
</div><div class="h2i16">
<h2>15 响应请求 Web应用程序</h2>
<ul>
<li>在你的终端上运行的应用程序非常适合你自己使用. 但是普通用户已经被互联网和万维网宠坏了. 他们不想为了使用你的应用而学习使用终端. 他们甚至不想安装你的应用, 他们希望在浏览器中单击某个链接就可以使用它</li>
<li>http 80端口</li>
<li>利用net/http包做了一个简单版的本地Web服务器</li>
</ul>
</div><div class="h2i17">
<h2>16 HTML模板</h2>
<ul>
<li>html/template包</li>
<li>html/template包基于text/template包. 使用这两个包的方法几乎完全相同, 但是html/template有一些额外的安全特性, 这是使用HTML所需要的</li>
<li>先看text/template包, 创建一个模板, 里面可以留想填内容的位置, 然后执行模版的时候把参数填到空位上</li>
<li>action {{.}}格式, 留空位</li>
<li>if action, {{if .}}可有可无{{end}}, 当传入true这部分显示, false不显示</li>
<li>range action, {{range .}}这是第{{.}}次{{end}}, 传入一个切片, 切片的内容会显示在里面的位置</li>
<li>{{.Name}}, 传入一个struct变量, 这里可以显示它的Name字段</li>
</ul>
</div><div class="h2i18">
<h2>A os.OpenFile</h2>
<ul>
<li>第二个参数, os.CREATE等, 把需要的模式用位运算符或连接</li>
<li>第三个参数, Unix样式的文件权限, rwx, 所有者同组和其他</li>
<ul>
<li>0644, 0代表8进制</li>
<li>Windows不像类Unix系统那样处理文件权限, 所以无论这个参数是什么, 都将在Windows上使用默认权限创建文件</li>
</ul>
<li>os.Open()只读, os.Create()创建任何用户可读写的文件, 如果你的需求是这样的, 优先使用这两个, 易读易理解</li>
</ul>
</div><div class="h2i19">
<h2>B 剩下的内容</h2>
<ul>
<li>if也可以添加一个变量初始化语句, 变量的作用域在if块内</li>
<ul>
<li>if err := myFunc(); err != nil {...}, 大多这么用</li>
</ul>
<li>switch语句, 默认break, 不想break加fallthrough</li>
<li>len(str)返回的是字节数, 想知道字符数要么转成rune[]然后len, 要么用unitcode/utf8包中的RuneCountInString函数</li>
<li>明白你的需求, 什么时候用byte, 什么时候用rune, for-range是rune, for-i是byte</li>
		</ul>
</div>	</div>
<script src="../style/js/particles.min.js"></script>
<script src="../style/js/particles.config.js"></script>
</body>
</html>
